package com.fcg.nginxlog.schedule;


import com.fcg.nginxlog.bean.AccessLog;
import com.fcg.nginxlog.dao.AccessLogDao;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * '$remote_addr - [$time_iso8601] "$request" $request_length '
 * '$status $body_bytes_sent rt=$request_time urt=$upstream_response_time '
 * '"$http_referer" "$http_x_forwarded_for" "$upstream_addr" '
 * '"$host" "$http_user_agent" $request_id'
 * <p>
 * 171.212.115.182 - [2020-04-02T09:57:57+08:00] "POST /app/user/public/loginByToken HTTP/1.1" 538 200 2017 rt=0.090 urt=0.090 "-" "-" "172.16.9.96:8080" "dsjapp.scgsdsj.com" "okhttp/3.12.0" 2cf4bb05402c569784b73ba009229d30
 *
 * @author FCG
 */
@Slf4j
@Component
public class AccessLogTask {

    @Resource
    private AccessLogDao logDao;

    @PostConstruct
    public void parseFile() {
        Path file = Paths.get("E:\\run\\dsjapp.access.log");
        try {
            List<AccessLog> logList = Files.lines(file).map(line -> {
                log.info(line);
                return AccessLogTask.parse(line);
            }).collect(Collectors.toList());
            log.info("日志总行数:{}", logList.size());
            AtomicInteger errorCnt = new AtomicInteger();
            List<AccessLog> errors = new ArrayList<>();
//            logList.forEach(accessLog -> {
//                try {
//                    int i = logDao.saveLog(accessLog);
//                    cnt.addAndGet(i);
//                } catch (Exception ex) {
//                    ex.printStackTrace();
//                    errors.add(accessLog);
//                }
//            });

            //分批次插入数据，切分为小list，size=100
            Lists.partition(logList, 10).forEach(subList -> {
                try {
                    int cnt = logDao.saveLogs(subList);
                    log.info("解析行数:{},存入DB行数:{}", subList.size(), cnt);
                } catch (Exception ex) {
                    ex.printStackTrace();
                    errors.addAll(subList);
                }
            });
            log.info("异常:{}", errors.size());
//            errors.forEach(System.out::println);
        } catch (IOException e) {
            e.printStackTrace();
        }
        log.info("over");
    }


    public static AccessLog parse(String line) {
        //正则表达式匹配日志
        String reg = "(?<remoteAddr>\\S+)\\s-\\s\\[(?<time>\\S+)]\\s\"(?<request>(?<method>\\w+)\\s(?<uri>\\S+)\\s(?<protocol>\\S+))\"" +
                "\\s(?<reqLen>\\d+)\\s(?<status>\\d+)\\s(?<rspLen>\\d+)\\srt=(?<requestTime>\\S+)\\surt=(?<upstreamResponseTime>\\S+)\\s\"(?<ref>\\S+)\"" +
                "\\s\"(?<xfd>\\S+)\"\\s\"(?<upstreamAddr>\\S+)\"\\s\"(?<host>\\S+)\"\\s\"(?<agent>.+)\"\\s(?<reqId>\\S+)";
        Pattern pattern = Pattern.compile(reg);
        Matcher matcher = pattern.matcher(line);
        //日期格式
        DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
        AccessLog accessLog = null;
        if (matcher.matches()) {
            //实例对象，利用反射填充字段值
            AccessLog instance = new AccessLog();
            ReflectionUtils.doWithFields(AccessLog.class, field -> {
                field.setAccessible(true);
                String name = field.getName();
                String groupVal = matcher.group(name);
                //去除 ‘ ’ 、‘-’
                String val = StringUtils.strip(StringUtils.strip(groupVal), "-");
//                log.debug("name:{}={}", name, val);
                Class<?> fieldType = field.getType();
                //根据字段类型，做数据转换
                if (StringUtils.isNotBlank(val)) {
                    if (fieldType.isAssignableFrom(LocalDateTime.class)) {
                        LocalDateTime time = LocalDateTime.parse(val, formatter);
                        field.set(instance, time);
                    } else if (fieldType.isAssignableFrom(Long.class)) {
                        long longVal = Long.parseLong(val);
                        field.set(instance, longVal);
                    } else if (fieldType.isAssignableFrom(Integer.class)) {
                        int intVal = Integer.parseInt(val);
                        field.set(instance, intVal);
                    } else if (fieldType.isAssignableFrom(Float.class)) {
                        float floatVal = Float.parseFloat(val);
                        field.set(instance, floatVal);
                    } else {
                        field.set(instance, val);
                    }
                }
            });
            accessLog = instance;
        } else {
            log.warn("输入字符串不匹配");
        }
        return accessLog;
    }

}
